package com.zy.cat.common.aspect;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.TimeUnit;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import com.zy.cat.common.annotation.CheckDevPermission;
import com.zy.cat.common.utils.AesEncryptUtils;
import com.zy.cat.entity.dev.CatDevUserInfo;
import com.zy.cat.entity.resp.RSPE;
import com.zy.cat.service.CacheService;
import com.zy.cat.service.CatDevService;

import lombok.extern.slf4j.Slf4j;

@Aspect
@Component
@Slf4j
public class CheckDevPermissionAspecct {
	private static final Set<String> KEY = new ConcurrentSkipListSet<>();

	@Autowired
	@Qualifier("redis0")
	RedisTemplate<String, String> redis0;

	@Autowired
	@Qualifier("redis1")
	RedisTemplate<String, Long> redis1;

	@Autowired
	CacheService cacheService;
	
	@Autowired
	CatDevService catDevService;

	private Object obj;

	@Around("@annotation(checkDevPermission)")
	public Object checking(ProceedingJoinPoint joinPoint, CheckDevPermission checkDevPermission) {
		String sign = duplicateKey(joinPoint);

		String pid = parseKey(joinPoint, checkDevPermission.PID(), "");
		String utoken = parseKey(joinPoint, checkDevPermission.UTOKEN(), "");
		String uuid = parseKey(joinPoint, "uuid", "");
		

//		if (StringUtils.isBlank(pid) || StringUtils.isBlank(utoken)||StringUtils.isBlank(uuid)) {
//			log.error("-账户认证失败-->pid-->"+pid);
//			log.error("-账户认证失败-->utoken-->"+utoken);
//			log.error("-账户认证失败-->uuid-->"+uuid);
//			return new RSPE<Object>(-1, "账户认证失败");
//		}

		/* 鉴权接口限制用户请求频率，每分钟600次以内,否则 */
		if (StringUtils.isNotBlank(pid)) {
			String key = "CAT_REQUEST_PID_LIMIT_" + pid;
			if (redis1.hasKey(key)) {
				long rtimes_600 = redis1.opsForHash().increment(key, "600", 1L);
				if (rtimes_600 > 600) {
					long rtimes_400 = redis1.opsForHash().increment(key, "400", 1L);
					if(rtimes_400==1) {
						redis1.expire(key, 1, TimeUnit.MINUTES);
						return new RSPE<Object>(-6666666, "请求频率异常，请稍后重试！否则将被拉入请求黑名单！");
					}
					if (rtimes_400 > 400) {
						long rtimes_200 = redis1.opsForHash().increment(key, "200", 1L);
						if(rtimes_200==1) {
							redis1.expire(key, 1, TimeUnit.MINUTES);
							return new RSPE<Object>(-6666666, "请求频率异常，请稍后重试！否则将被拉入请求黑名单！");
						}
						if (rtimes_200 > 200) {
							long rtimes_100 = redis1.opsForHash().increment(key, "100", 1L);
							if(rtimes_100==1) {
								redis1.expire(key, 1, TimeUnit.MINUTES);
								return new RSPE<Object>(-6666666, "请求频率异常，请稍后重试！否则将被拉入请求黑名单！");
							}
							if (rtimes_100 > 100) {
								long rtimes_60 = redis1.opsForHash().increment(key, "60", 1L);
								if(rtimes_60==1) {
									redis1.expire(key, 1, TimeUnit.MINUTES);
									return new RSPE<Object>(-6666666, "请求频率异常，请稍后重试！否则将被拉入请求黑名单！");
								}
								if (rtimes_60 > 60) {
									redis1.expire(key, 30, TimeUnit.MINUTES);
									return new RSPE<Object>(-6666666, "请求频率异常，已被拉入请求黑名单！请30分钟后重试");
								}
							}
						}
					}
				}
			} else {
				redis1.opsForHash().increment(key, "600", 1L);
				redis1.expire(key, 1, TimeUnit.MINUTES);
			}
		}

//		if (StringUtils.isBlank(pid) || StringUtils.isBlank(utoken)) {
//			return new RSPE<Object>(4001, "认证信息不能为空");
//		}
//
//		String redis_pid = AesEncryptUtils.decryptBySalt(utoken);
//		if (redis_pid == null) {
//			return new RSPE<Object>(4002, "认证信息错误");
//		}
//
//		if (!redis_pid.equals(pid)) {
//			return new RSPE<Object>(4003, "认证信息非法");
//		}

//		Object loclUtoken = redis0.opsForHash().get("CAT_DEV_LOGIN_TOKEN", pid);
//		if (loclUtoken == null || !loclUtoken.equals(utoken)) {
//			return new RSPE<Object>(4004, "登录已失效");
//		}

//		CatDevUserInfo user = catDevService.getCatDevUserInfoByPid(pid);
//		if (user == null) {
//			return new RSPE<Object>(4005, "认证账户非法");
//		}

//      需要重新登录		
//		4001：认证信息不能为空
//		4002：认证信息错误
//		4003：认证信息非法
//		4004：登录已失效
//		4005：认证账户非法
//		4006：账户已冻结！请联系客服！
//		4007：账户审核中！
//		4008：账户异常！请联系客服！
//		
//		需要重新请求
//		-666：请求过于频繁，请稍后重试
//		-999：连接超时，请稍后重试！
//		if (user.getIstatus() == -1) {
//			return new RSPE<Object>(4006, "账户已冻结！请联系客服！");
//		} else if (user.getIstatus() == 0) {
//			return new RSPE<Object>(4007, "账户审核中！");
//		}
//		if (user.getIstatus() != 1) {
//			return new RSPE<Object>(4008, "账户异常！请联系客服！");
//		}

		if (!KEY.add(sign)) {
			return new RSPE<Object>(-666, "请求过于频繁，请稍后重试！");
		} else {
			try {
				obj = joinPoint.proceed();
			} catch (Throwable e) {
				log.error(e.getLocalizedMessage());
				return new RSPE<Object>(-999, "连接超时，请稍后重试！");
			} finally {
				KEY.remove(sign);
			}
		}
		return obj;

//		// 获取传入目标方法的参数
//		String pid = parseKey(joinPoint, checkPddDevPermission.PID(), "");
//		String utoken = parseKey(joinPoint, checkPddDevPermission.UTOKEN(), "");
//		
//		log.info("-pid-->{}",pid);
//		log.info("-utoken-->{}",utoken);
//
//		if (StringUtils.isBlank(pid) || StringUtils.isBlank(utoken)) {
//			return new RSPE<Object>(4001, "认证参数不能为空！", "");
//		}
//		
//		String dpid = AesEncryptUtils.decryptBySalt(utoken);
//		if (dpid == null) {
//			return new RSPE<Object>(4002, "用户令牌错误！", "");
//		}
//		if(!dpid.equals(pid)) {
//			return new RSPE<Object>(4003, "用户令牌不匹配！", "");
//		}
//		Object loclUtoken = redis0.opsForHash().get("PDD_DEV_LOGIN_TOKEN", pid);
//		if (loclUtoken == null) {
//			return new RSPE<Object>(4003, "登录已失效！", "");
//		}
//		
//		PddDevUserInfo user=cacheService.getPddDevUserInfoByPid(pid);
//		if (user==null|| !utoken.equals(loclUtoken.toString())) {
//			return new RSPE<Object>(4003, "登录已失效！", "");
//		}
//		
//		if (!KEY.add(sign)) {
//			return new RSPE<Object>(-666, "请求过于频繁，请稍后重试！", "");
//		}else {
//			try {
//				obj = joinPoint.proceed();
//			} catch (Throwable e) {
//				log.error(e.getLocalizedMessage());
//				return new RSPE<Object>(-999, "连接超时，请稍后重试！", "");
//			} finally {
//				KEY.remove(sign);
//			}	
//		}
//		return obj;
	}

	/**
	 * @info 获取缓存的key对应的值
	 * @author Link
	 * @date 2019-07-05 14:18
	 * @param joinPoint
	 * @param spel
	 * @param defaultValuue
	 * @return
	 */
	private String parseKey(JoinPoint joinPoint, String spel, String defaultValuue) {
		String result = "";
		// 获取参数的类型
		Method method = null;
		Object[] args = joinPoint.getArgs();
		spel = spel.replaceAll("\\s*", "");
		try {
			MethodSignature signature = (MethodSignature) joinPoint.getSignature();
			method = signature.getMethod();
		} catch (SecurityException e) {
			e.printStackTrace();
			return defaultValuue;
		}

		Map<String, Object> map = new HashMap<String, Object>();

		// 获取被拦截方法参数名列表(使用Spring支持类库)
		LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
		String[] paraNameArr = u.getParameterNames(method);
		if (paraNameArr==null) {
			log.error("methodName:"+method.getName());
			log.error("spel:"+spel);	
			log.error("methodparaCount:"+method.getParameterCount());
			log.error("methodparaName:"+method.getParameters());
		}
		
		// 把方法参数放入SPEL上下文中
		for (int i = 0; i < paraNameArr.length; i++) {
//			log.info(paraNameArr[i]+"<------->"+args[i]);
			if (args[i] == null) {
				map.put(paraNameArr[i], "");
			} else {
				map.put(paraNameArr[i], args[i]);
			}

		}
		for (String str : spel.split("_")) {
			String val = "";
			Object obj = map.get(str);
			if (obj != null) {
				val = obj.toString();
			}
			if (StringUtils.isBlank(result)) {
				result = val;
			} else {
				result += "_" + val;
			}
		}
		return result;
	}

	/**
	 * 
	 * @info 访问是否受限制
	 * @author Link
	 * @date 2019-08-15 15:45
	 * @param pjp
	 * @return
	 * @throws Throwable
	 */
	private String duplicateKey(JoinPoint pjp) {
		MethodSignature msig = (MethodSignature) pjp.getSignature();
		Method currentMethod = null;
		try {
			currentMethod = pjp.getTarget().getClass().getMethod(msig.getName(), msig.getParameterTypes());
		} catch (NoSuchMethodException e) {
		} catch (SecurityException e) {
		}
		// 拼接签名
		StringBuilder sb = new StringBuilder(currentMethod.toString());
		Object[] args = pjp.getArgs();
		for (Object object : args) {
			if (object != null) {
				sb.append(object.getClass().toString());
				sb.append(object.toString());
			}
		}
		String sign = sb.toString();
		return sign;
	}
}
